/*
'*' - CLASS/MODULE
''  - parametros da entidade 
'!' - parametros extra nao persistidos (qualquer tipo)
'#' - metodos extra da entidade
'@' - metodos do controller
*/

#include "myfile.h"
#include "properties.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

Property **readProperties(char *fname, int *numPropertiesRead)
{
  int propertiesSize=0;
  Property **properties = 0;
  int numProperties=0;
  Property *p;

  while(p=getProperty("teste.properties",numProperties))
  {
    if(numProperties == propertiesSize)
    {
      Property **newProperties = (Property **)realloc(properties,sizeof(Property *)*(propertiesSize+16));
      if(newProperties)
      {
        properties = newProperties;
        propertiesSize += 16;
      }
    }
    properties[numProperties]=p;
    ++numProperties;
  }

  if(numPropertiesRead) *numPropertiesRead = numProperties;
  return properties;
}

char *findProperty(char *name, Property **properties, int numProperties)
{
  int i;

  for(i=0; i<numProperties; i++)
  {
    if(!strcmp(properties[i]->name,name)) return properties[i]->value;
  }

  return 0;
}

char *createFileName(char *module, char *class, char *extension)
{
  char *fname = 0;

  if(module) {
    int len = strlen(module) + strlen(class) + strlen(extension) + 3;
    fname = (char *)malloc(sizeof(char)*len);
    if(fname) sprintf(fname,"%s_%s.%s",module,class,extension);
  } else {
    int len = strlen(class) + strlen(extension) + 2;
    fname = (char *)malloc(sizeof(char)*len);
    if(fname) sprintf(fname,"%s.%s",class,extension);
  }

  return fname;
}

typedef struct
{
  char *key;
  char *val;
} CharMap;

char *findMapVal(char *key, CharMap *map)
{
  int i=0;

  while(map[i].key)
  {
    if(!strcmp(key,map[i].key)) return map[i].val;
    ++i;
  }

  return 0;
}

char *typeToC(char *type)
{
  CharMap map[] = {
    {"CHAR", "char"},
    {"INT16", "int16_t"},
    {"INT32", "int32_t"},
    {"INT64", "int64_t"},
    {"REAL", "double"},
    {"ID", "int64_t"},
    {"PCHAR", "char*"},
    {0, 0}
  };

  return findMapVal(type,map);
}

char *typeToGVar(char *type)
{
  CharMap map[] = {
    {"CHAR", "charVal"},
    {"INT16", "int16Val"},
    {"INT32", "int32Val"},
    {"INT64", "int64Val"},
    {"REAL", "doubleVal"},
    {"ID", "int64Val"},
    {"PCHAR", "pcharVal"},
    {0, 0}
  };

  return findMapVal(type,map);
}

void printGetFunctionDecl(MYFile file, char *modulo, char *classe, char *ctype, char *name)
{
  if(modulo) printFile(file,"static %s get_%s(%s_%s *this)",ctype,name,modulo,classe);
  else printFile(file,"static %s get_%s(%s *this)",ctype,name,modulo);
}


void printGetEntityFunctionDecl(MYFile file, char *modulo, char *classe, char *name)
{
  char *typeAndName = &name[3];
  if(modulo) printFile(file,"static %s* get_%s(%s_%s *this)",typeAndName,typeAndName,modulo,classe);
  else printFile(file,"static %s* get_%s(%s *this)",typeAndName,typeAndName,classe);
}

void printSetFunctionDecl(MYFile file, char *modulo, char *classe, char *ctype, char *name)
{
  if(modulo) printFile(file,"static void set_%s(%s_%s *this, %s novo)",name,modulo,classe,ctype);
  else printFile(file,"static void set_%s(%s *this, %s novo)",name,classe,ctype);
}

void printSetEntityFunctionDecl(MYFile file, char *modulo, char *classe, char *name)
{
  char *typeAndName = &name[3];
  if(modulo) printFile(file,"static void set_%s(%s_%s *this, %s *novo)",typeAndName,modulo,classe,typeAndName);
  else printFile(file,"static void set_%s(%s *this, %s *novo)",typeAndName,classe,typeAndName);
}

void printExtraFunctionDecl(MYFile file, char *modulo, char *classe, char *name, char *retAndArgs)
{
  char *ret = retAndArgs;
  char *args;

  args = strstr(retAndArgs,",");
  if(args)
  {
    args[0] = 0; ++args;
    if(modulo) printFile(file,"static %s %s(%s_%s *this, %s)",ret,name,modulo,classe,args);
    else printFile(file,"static %s %s(%s *this, %s)",ret,name,classe,args);  
    args--; args[0] = ',';
  }
  else
  {
    if(modulo) printFile(file,"static %s %s(%s_%s *this)",ret,name,modulo,classe);
    printFile(file,"static %s %s(%s *this)",ret,name,classe);
  }
}

void printInterfaceField(MYFile file, char *modulo, char *classe, char *ctype, char *name)
{
  if(modulo) printFile(file,"  %s (*get_%s)(%s_%s *this);\n",ctype,name,modulo,classe);
  else printFile(file,"  %s (*get_%s)(%s *this);\n",ctype,name,classe);

  if(strstr(name,"id_")==name) {
    char *typeAndName = &name[3];
    if(modulo) printFile(file,"  %s* (*get_%s)(%s_%s *this);\n",typeAndName,typeAndName,modulo,classe);
    else printFile(file,"  %s* (*get_%s)(%s *this);\n",typeAndName,typeAndName,classe);
  }

  if(modulo) printFile(file,"  void (*set_%s)(%s_%s *this, %s novo);\n",name,modulo,classe,ctype);
  else printFile(file,"  void (*set_%s)(%s *this, %s novo);\n",name,classe,ctype);

  if(strstr(name,"id_")==name) {
    char *typeAndName = &name[3];
    if(modulo) printFile(file,"  void (*set_%s)(%s_%s *this, %s *novo);\n",typeAndName,modulo,classe,typeAndName);
    else printFile(file,"  void (*set_%s)(%s *this, %s *novo);\n",typeAndName,classe,typeAndName);
  }
}

void printExtraFunctionField(MYFile file, char *modulo, char *classe, char *name, char *retAndArgs)
{
  char *ret = retAndArgs;
  char *args;

  args = strstr(retAndArgs,",");
  if(args)
  {
    args[0] = 0; ++args;
    if(modulo) printFile(file,"  %s (*%s)(%s_%s *this, %s);\n",ret,name,modulo,classe,args);
    else printFile(file,"  %s (*%s)(%s *this, %s);\n",ret,name,classe,args);  
    args--; args[0] = ',';
  }
  else
  {
    if(modulo) printFile(file,"  %s (*%s)(%s_%s *this);\n",ret,name,modulo,classe);
    else printFile(file,"  %s (*%s)(%s *this);\n",ret,name,classe);
  }
}

void printGetEntityFunctionImpl(MYFile file, char *modulo, char *classe, char *name)
{
  char *nameAndType = &name[3];
  printFile(file,"\n{\n  if(!this->%sPtr) this->%sPtr=loadEntity(%s,this->%s);\n  return this->%sPtr;\n}\n\n",
            nameAndType,nameAndType,nameAndType,name,nameAndType);
}

void printSetEntityFunctionImpl(MYFile file, char *modulo, char *classe, char *name)
{
  char *nameAndType = &name[3];
  printFile(file,"\n{\n  this->%sPtr = novo;\n  this->%s = getId(novo);\n}\n\n",nameAndType,name);
}

void printGetFunctionImpl(MYFile file, char *modulo, char *classe, char *ctype, char *name)
{
  printFile(file,"\n{\n  return this->%s;\n}\n\n",name);
  if(strstr(name,"id_")==name)
  {
    printGetEntityFunctionDecl(file, modulo, classe, name);
    printGetEntityFunctionImpl(file, modulo, classe, name);
  }
}

void printSetFunctionImpl(MYFile file, char *modulo, char *classe, char *ctype, char *name)
{
  if(strstr(name,"id_")==name)
  {
    printFile(file,"\n{\n  this->%sPtr = 0;\n  this->%s = novo;\n}\n\n",name+3,name);
    printSetEntityFunctionDecl(file, modulo, classe, name);
    printSetEntityFunctionImpl(file, modulo, classe, name);
  }
  else printFile(file,"\n{\n  this->%s = novo;\n}\n\n",name);
}

void printControllerFunctionDecl(MYFile file, char *modulo, char *classe, char *name, char *retAndArgs)
{
  char *ret = retAndArgs;
  char *args;

  args = strstr(retAndArgs,",");
  if(args)
  {
    args[0] = 0; ++args;
    if(modulo) printFile(file,"%s %s_%s_%s(%s)",ret,modulo,classe,name,args);
    else printFile(file,"%s %s_%s(%s)",ret,classe,name,args);  
    args--; args[0] = ',';
  }
  else
  {
    if(modulo) printFile(file,"%s %s_%s_%s()",ret,modulo,classe,name);
    else printFile(file,"%s %s_%s()",ret,classe,name);
  }
}


void createH(Property **properties, int numProperties)
{
  int i;
  char *classe;
  char *modulo;

  classe = findProperty("*CLASS",properties,numProperties);
  if(!classe) return;

  modulo = findProperty("*MODULE",properties,numProperties);

  char *fname = createFileName(modulo,classe,"h");
  if(!fname) return;

  MYFile file = openFile(fname,MWriteCreate);
  free(fname);
  if(!validFile(file)) return;

//ifdef/define 
  if(modulo)
  {
    printFile(file,"#ifndef %s_%s_h\n",modulo,classe);
    printFile(file,"#define %s_%s_h\n\n",modulo,classe);
  }
  else
  {
    printFile(file,"#ifndef %s_h\n",classe);
    printFile(file,"#define %s_h\n\n",classe);
  }

//includes
  printFile(file,"#include <stdint.h>\n");

//definicao da estrutura (atributos)
  printFile(file,"\n\ntypedef struct {\n");
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]=='*') continue;
    if(p->name[0]=='#') continue;
    if(p->name[0]=='@') continue;

    char *name = p->name;
    char *ctype;

    if(name[0]=='!'){name++; ctype=p->value;}//parametros extra comecam com !, e podem ser de qq tipo
    else ctype = typeToC(p->value);

    if(!ctype) continue;

    printFile(file,"  %s %s;\n",ctype,name);
    if(strstr(name,"id_")==name)
    {
      char *typeAndName = &name[3];
      printFile(file,"  %s* %sPtr;\n",typeAndName,typeAndName);
    }
  }
  printFile(file,"} ");
  if(modulo) printFile(file,"%s_",modulo);
  printFile(file,"%s;\n\n",classe);

//definicao da interface (metodos)
  printFile(file,"\n\ntypedef struct {\n");

  if(modulo) printFile(file,"  %s_%s* (*create)();\n",modulo,classe);
  else printFile(file,"  %s* (*create)();\n",modulo,classe);

  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]=='*') continue;
    if(p->name[0]=='@') continue;

    if(p->name[0]=='#') printExtraFunctionField(file,modulo,classe,p->name+1,p->value);
    else
    {
      char *name = p->name;
      char *ctype;

      if(name[0]=='!'){name++; ctype=p->value;}//parametros extra comecam com !, e podem ser de qq tipo
      else ctype = typeToC(p->value);

      if(!ctype) continue;

      printInterfaceField(file,modulo,classe,ctype,name);
    }
  }
  printFile(file,"} ");

//declaracao da interface
  if(modulo)
  {
    printFile(file,"%s_%sInterface;\n\n",modulo,classe);
    printFile(file,"extern %s_%sInterface %s_%sI;\n\n",modulo,classe,modulo,classe);
  }
  else
  {
    printFile(file,"%sInterface;\n\n",classe);
    printFile(file,"extern %sInterface %sI;\n\n",classe,classe);
  }

  printFile(file,"#endif\n");

  closeFile(file);
}

void createC(Property **properties, int numProperties)
{
  int i;
  char *classe;
  char *modulo;

  classe = findProperty("*CLASS",properties,numProperties);
  if(!classe) return;

  modulo = findProperty("*MODULE",properties,numProperties);

  char *fname = createFileName(modulo,classe,"c");
  if(!fname) return;

  MYFile file = openFile(fname,MWriteCreate);
  free(fname);
  if(!validFile(file)) return;

//includes
  if(modulo) printFile(file,"#include \"%s_%s.h\"\n\n",modulo,classe);
  else printFile(file,"#include \"%s.h\"\n\n",classe);

//declaracao dos metodos
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]=='*') continue;
    if(p->name[0]=='@') continue;

    if(p->name[0]=='#')
    {
      printExtraFunctionDecl(file,modulo,classe,p->name+1,p->value);
      printFile(file,";\n");
    }
    else
    {
      char *name = p->name;
      char *ctype;

      if(name[0]=='!'){name++; ctype=p->value;}//parametros extra comecam com !, e podem ser de qq tipo
      else ctype = typeToC(p->value);

      if(!ctype) continue;

      printGetFunctionDecl(file,modulo,classe,ctype,name);
      printFile(file,";\n");
      printSetFunctionDecl(file,modulo,classe,ctype,name);
      printFile(file,";\n");
      if(strstr(name,"id_")==name)
      {
        printGetEntityFunctionDecl(file,modulo,classe,name);
        printFile(file,";\n");
        printSetEntityFunctionDecl(file,modulo,classe,name);
        printFile(file,";\n");
      }
    }
  }
  printFile(file,"\n");

//implementacao do create()  
  if(modulo) printFile(file,"static %s_%s* create()\n{\n  %s_%s *ptr = malloc(sizeof(%s_%s));\n  if(!ptr) return 0;\n  memset(ptr,0,sizeof(%s_%s));\n",modulo,classe,modulo,classe,modulo,classe,modulo,classe);
  else printFile(file,"static %s* create()\n{\n  %s *ptr = malloc(sizeof(%s));\n  if(!ptr) return 0;\n  memset(ptr,0,sizeof(%s));\n",classe,classe,classe,classe);
  printFile(file,"  return ptr;\n}\n\n");

//implementacao dos metodos
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]=='*') continue;
    if(p->name[0]=='@') continue;

    if(p->name[0]=='#')
    {
      printExtraFunctionDecl(file,modulo,classe,p->name+1,p->value);
      printFile(file,"\n{\n\n}\n\n");
    }
    else
    {
      char *name = p->name;
      char *ctype;

      if(name[0]=='!'){name++; ctype=p->value;}//parametros extra comecam com !, e podem ser de qq tipo
      else ctype = typeToC(p->value);

      if(!ctype) continue;

      printGetFunctionDecl(file,modulo,classe,ctype,name);
      printGetFunctionImpl(file,modulo,classe,ctype,name);
      printSetFunctionDecl(file,modulo,classe,ctype,name);
      printSetFunctionImpl(file,modulo,classe,ctype,name);
    }
  }
  printFile(file,"\n");

//inicializacao da interface
  if(modulo)
  {
    printFile(file,"%s_%sInterface %s_%sI = {\n",modulo,classe,modulo,classe);
  }
  else
  {
    printFile(file,"extern %sInterface %sI = {\n",classe,classe);
  }
  printFile(file,"create");
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]=='*') continue;
    if(p->name[0]=='@') continue;

    if(p->name[0]=='#')
    {
      printFile(file,",\n%s",p->name+1);
    }
    else
    {
      char *name = p->name;
      char *ctype;

      if(name[0]=='!'){name++; ctype=p->value;}//parametros extra comecam com !, e podem ser de qq tipo
      else ctype = typeToC(p->value);

      if(!ctype) continue;

      printFile(file,",\nget_%s",name);

      if(strstr(name,"id_")==name)
      {
        printFile(file,",\nget_%s",name+3);
      }

      printFile(file,",\nset_%s",name);

      if(strstr(name,"id_")==name)
      {
        printFile(file,",\nset_%s",name+3);
      }
    }
  }
  printFile(file,"\n};\n");

  closeFile(file);
}

void createControllerH(Property **properties, int numProperties)
{
  int i;
  char *classe;
  char *modulo;

  classe = findProperty("*CLASS",properties,numProperties);
  if(!classe) return;

  modulo = findProperty("*MODULE",properties,numProperties);

  char *fname = createFileName(modulo,classe,"controller.h");
  if(!fname) return;

  MYFile file = openFile(fname,MWriteCreate);
  free(fname);
  if(!validFile(file)) return;

//ifdef/define
  if(modulo)
  {
    printFile(file,"#ifndef %s_%s_controller_h\n",modulo,classe);
    printFile(file,"#define %s_%s_controller_h\n\n",modulo,classe);
    printFile(file,"#include \"%s_%s.h\"\n\n",modulo,classe);
  }
  else
  {
    printFile(file,"#ifndef %s_controller_h\n",classe);
    printFile(file,"#define %s_controller_h\n\n",classe);
    printFile(file,"#include \"%s.h\"\n\n",classe);
  }

//declaracao dos metodos do controller
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]!='@') continue;

    printControllerFunctionDecl(file, modulo, classe, p->name+1, p->value);
    printFile(file,";\n\n");
  }

  printFile(file,"#endif\n");

  closeFile(file);
}

void createControllerC(Property **properties, int numProperties)
{
  int i;
  char *classe;
  char *modulo;

  classe = findProperty("*CLASS",properties,numProperties);
  if(!classe) return;

  modulo = findProperty("*MODULE",properties,numProperties);

  char *fname = createFileName(modulo,classe,"controller.c");
  if(!fname) return;

  MYFile file = openFile(fname,MWriteCreate);
  free(fname);
  if(!validFile(file)) return;

//includes
  if(modulo) printFile(file,"#include \"%s_%s_controller.h\"\n\n",modulo,classe);
  else printFile(file,"#include \"%s_controller.h\"\n\n",classe);

//implementacao dos metodos do controller
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]!='@') continue;

    printControllerFunctionDecl(file, modulo, classe, p->name+1, p->value);
    printFile(file,"\n{\n\n}\n\n");
  }

//exportando metodos do controller
  int n=0;
  printFile(file,"DECL(\n");
  for(i=0; i<numProperties; i++)
  {
    Property *p = properties[i];
    if(p->name[0]!='@') continue;

    if(n) printFile(file,",\n");
    ++n;
    printFile(file,"  M(%s)",p->name+1);
  }
  printFile(file,"\n)\n");

  closeFile(file);
}

void main(void)
{
  int i;
  int numProperties;

  Property **properties = readProperties("teste.properties",&numProperties);

  createH(properties,numProperties);
  createC(properties,numProperties);

  createControllerH(properties,numProperties);
  createControllerC(properties,numProperties);

  for(i=0; i<numProperties; i++) delProperty(properties[i]);
  free(properties);
}
